package com.novoda.runbuddy.fixture;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.StreamTokenizer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;

import android.location.Location;

import com.geekchique.runningbuddy.activity.stopwatch.StopwatchTools;
import com.geekchique.runningbuddy.model.Route;
import com.geekchique.runningbuddy.model.RouteSegment;
import com.geekchique.runningbuddy.model.Run;
import com.geekchique.runningbuddy.model.RunSegment;

public class FixtureCreator {

	private static final String	ROUTE_ID				= "ROUTE_ID";
	private static final String	ROUTE_SID				= "ROUTE_SID";
	private static final String	ROUTE_LOCATIONS			= "ROUTE_LOCATIONS";
	private static final String	ROUTE_GEOLOCATION_ID	= "ROUTE_GEOLOCATION_ID";
	private static final String	ROUTE_NAME				= "ROUTE_NAME";
	private static final String	ROUTE_CREATED_AT		= "ROUTE_CREATED_AT";
	private static final String	ROUTE_MODIFIED_AT		= "ROUTE_MODIFIED_AT";
	private Map<String, Route>	routeFixtures			= new HashMap<String, Route>();

	public void collectAllFixtures() {
		try {
			loadRouteFixturesFromMockLocationProviders();
			loadRouteFixturesFromFlatFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public Route getRoute(String route) {
		return routeFixtures.get(route);
	}

	public Route readRoute(String routeFile) throws IOException {
		Properties routeFixture = new Properties();

		FileInputStream fileInputStream = new FileInputStream(routeFile);
		routeFixture.load(fileInputStream);

		Integer routeId = Integer.parseInt(routeFixture.get(ROUTE_ID).toString());
		Integer routeSid = Integer.parseInt(routeFixture.get(ROUTE_SID).toString());
		Integer routeGeoId = Integer.parseInt(routeFixture.get(ROUTE_GEOLOCATION_ID).toString());
		Long createdAt = Long.parseLong(routeFixture.get(ROUTE_CREATED_AT).toString());
		Long modifiedAt = Long.parseLong(routeFixture.get(ROUTE_MODIFIED_AT).toString());

		return new Route(routeId.intValue(), routeSid.intValue(), routeGeoId, (String) routeFixture.get(ROUTE_NAME), new LinkedList<RouteSegment>(), createdAt.longValue(), modifiedAt.longValue());
	}

	@SuppressWarnings("deprecation")
	protected Vector<Location> getLocationsFromTokenizedFlatFile(String locations) {

		Vector<Location> allLocations = new Vector<Location>();
		Location newLocation;
		long lnum;
		double dnum;

		try {
			InputStream in = new ByteArrayInputStream(locations.getBytes());
			StreamTokenizer st = new StreamTokenizer(in);
			st.parseNumbers();
			st.wordChars('_', '_');
			st.eolIsSignificant(true);

			st.slashSlashComments(true);
			st.slashStarComments(true);

			// Parse the file
			int token = st.nextToken();
			while (token != StreamTokenizer.TT_EOF) {
				newLocation = new Location();

				lnum = (long) st.nval;
				newLocation.setTime(lnum);

				token = st.nextToken();
				token = st.nextToken();

				dnum = (double) st.nval;
				newLocation.setLongitude(dnum);

				token = st.nextToken();
				token = st.nextToken();

				dnum = (double) st.nval;
				newLocation.setLatitude(dnum);

				token = st.nextToken();
				token = st.nextToken();

				dnum = (double) st.nval;
				newLocation.setAltitude(dnum);
				token = st.nextToken();

				allLocations.add(newLocation);

				token = st.nextToken();
			}
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return allLocations;
	}

	protected int countNoOfDirectories(String locationDir) {

		int noOfDirectoriesUnderLocation = 0;
		File dir = new File(locationDir);
		String[] children = null;

		if (dir.isDirectory()) {

			children = dir.list();
			for (int i = 0; i < children.length; i++) {
				File underdirectory = new File(locationDir + "/" + children[i]);

				if (underdirectory.isDirectory() & notSVNFolder(underdirectory.getName())) {
					noOfDirectoriesUnderLocation++;
				}
			}

		} else {
			System.out.println("The MockLocationProvider Path is not set correctly");
		}

		return noOfDirectoriesUnderLocation;
	}

	protected LinkedList<Location> readTrack(String trackFile) throws IOException {

		BufferedReader in = new BufferedReader(new FileReader(trackFile));
		LinkedList<Location> locations = new LinkedList<Location>();

		String str;
		while ((str = in.readLine()) != null) {
			locations.add(extractLocationFromTrackLine(str));
		}
		in.close();

		return locations;

	}

	protected void loadRouteFixturesFromMockLocationProviders() throws IOException {
		LinkedList<Location> locations;
		int noOfProviders = countNoOfDirectories(FixtureList.LOCATION_PROVIDER_STUB_URI);
		LinkedList<String> directories = getDirectoriesNames(FixtureList.LOCATION_PROVIDER_STUB_URI);

		for (int name = 0; name < noOfProviders; name++) {

			locations = readTrack(FixtureList.LOCATION_PROVIDER_STUB_URI + directories.get(name) + "/" + FixtureList.FIXTURE_TRACK);
			Route route = readRoute(FixtureList.LOCATION_PROVIDER_STUB_URI + directories.get(name) + "/"
					+ FixtureList.FIXTURE_ROUTE);

			// For Just Now I've made a vector here but I think this makes more
			// sense as a linked list but Just was working on something with
			// vectors
			Vector<Location> locationsV = new Vector<Location>();
			for (Location location : locations) {
				locationsV.add(location);
			}

			route.locations = locationsV;
			routeFixtures.put(route.name, route);
		}
	}

	protected LinkedList<String> getDirectoriesNames(String parentDir) {
		int noOfDirectoriesUnderLocation = 0;
		LinkedList<String> directories = new LinkedList<String>();
		File dir = new File(parentDir);
		String[] children = null;

		if (dir.isDirectory()) {

			children = dir.list();
			for (int i = 0; i < children.length; i++) {
				File underdirectory = new File(parentDir + "/" + children[i]);

				if (underdirectory.isDirectory() & notSVNFolder(underdirectory.getName())) {
					noOfDirectoriesUnderLocation++;
					directories.add(children[i]);
				}
			}

		} else {
			System.out.println("The MockLocationProvider Path is not set correctly");
		}

		return directories;
	}

	boolean notSVNFolder(String folderName) {
		return !folderName.contains(".svn");
	}

	protected Location extractLocationFromTrackLine(String lineInTrackFile) {

		String spaces = " ";
		String[] fields = lineInTrackFile.split(spaces);

		long time = Long.parseLong(fields[0]);
		double longitude = Double.parseDouble(fields[1]);
		double latitude = Double.parseDouble(fields[2]);
		double altitude = Double.parseDouble(fields[3]);

		Location location = new Location();
		location.setTime(time);
		location.setLatitude(latitude);
		location.setLongitude(longitude);
		location.setAltitude(altitude);

		return location;
	}

	protected void loadRouteFixturesFromFlatFile() throws IOException {
		Properties routeFixture = new Properties();
		Route route;
		FileInputStream fileInputStream = new FileInputStream(FixtureList.FLATROUTE_GLASGOW_GREEN_AND_BACK);
		routeFixture.load(fileInputStream);

		Vector<Location> locations = getLocationsFromTokenizedFlatFile(routeFixture.get(ROUTE_LOCATIONS).toString());

		Integer routeId = Integer.parseInt(routeFixture.get(ROUTE_ID).toString());
		Integer routeSid = Integer.parseInt(routeFixture.get(ROUTE_SID).toString());
		Integer routeGeoId = Integer.parseInt(routeFixture.get(ROUTE_GEOLOCATION_ID).toString());
		Long createdAt = Long.parseLong(routeFixture.get(ROUTE_CREATED_AT).toString());
		Long modifiedAt = Long.parseLong(routeFixture.get(ROUTE_MODIFIED_AT).toString());

		route = new Route(routeId.intValue(), routeSid.intValue(), routeGeoId, (String) routeFixture.get(ROUTE_NAME), locations,
				createdAt.longValue(), modifiedAt.longValue());
		routeFixtures.put(route.name, route);

	}

	public Run getRun(String run) {
		Route route = routeFixtures.get(run);

		int FIRST_ENTRY = 0;
		int LAST_ENTRY = route.locations.size() - 1;
		long start = route.locations.get(FIRST_ENTRY).getTime();
		long totalTime = StopwatchTools.getDiffMilli(start, route.locations.get(LAST_ENTRY).getTime());

		return new Run(route._id, route, route.name, totalTime, start);
	}
}
